package com.wt.springsamples.es.client;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;

/**
 * ES8默认启用了安全配置，首次启动server时会在控制台生成内建超级用户elastic的密码、CA的指纹信息，Kibana需要的token（30分钟内有效）
 * 并且在客户端连接es服务端时需要使用https连接
 */
public class Es8SecureTest {

    public static void main(String[] args) throws IOException {
        String fingerprint = "b7db89b970804dfb75d1a60302a01e811a12e93e2e1865c00479194fee921f74";

        //官网例子中的TransportUtils在elasticsearch-java版本8中才存在
        //SSLContext sslContext = TransportUtils.sslContextFromCaFingerprint(fingerprint);
        SSLContext sslContext = sslContextFromCaFingerprint(fingerprint);

        //
        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
                AuthScope.ANY, new UsernamePasswordCredentials("elastic", "eIu7*gbSUDjSdHQQyTyc")
        );

        RestClient restClient = RestClient
                .builder(new HttpHost("localhost", 9200, "https"))
                .setHttpClientConfigCallback(hc -> hc
                        .setSSLContext(sslContext)
                        .setDefaultCredentialsProvider(credentialsProvider)
                )
                .build();

        // And create the API client
        ElasticsearchClient esClient = new ElasticsearchClient(new RestClientTransport(
                restClient, new JacksonJsonpMapper()));

//        esClient.indices().create(c -> c
//                .index("products")
//        );
        SearchResponse<Product> search = esClient.search(s -> s
                        .index("products")
                        .query(q -> q
                                .term(t -> t
                                        .field("name")
                                        .value(v -> v.stringValue("bicycle"))
                                )),
                Product.class);

        for (Hit<Product> hit: search.hits().hits()) {
//            processProduct(hit.source());
        }

        restClient.close();
    }

    public static SSLContext sslContextFromCaFingerprint(String fingerPrint) {
        fingerPrint = fingerPrint.replace(":", "");
        int len = fingerPrint.length();
        final byte[] fpBytes = new byte[len / 2];

        for(int i = 0; i < len; i += 2) {
            fpBytes[i / 2] = (byte)((Character.digit(fingerPrint.charAt(i), 16) << 4) + Character.digit(fingerPrint.charAt(i + 1), 16));
        }

        try {
            X509TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    throw new CertificateException("This is a client-side only trust manager");
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    X509Certificate anchor = chain[chain.length - 1];

                    byte[] bytes;
                    try {
                        MessageDigest md = MessageDigest.getInstance("SHA-256");
                        md.update(anchor.getEncoded());
                        bytes = md.digest();
                    } catch (NoSuchAlgorithmException var6) {
                        throw new RuntimeException(var6);
                    }

                    if (!Arrays.equals(fpBytes, bytes)) {
                        throw new CertificateException("Untrusted certificate: " + anchor.getSubjectX500Principal());
                    }
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init((KeyManager[])null, new X509TrustManager[]{tm}, (SecureRandom)null);
            return sslContext;
        } catch (KeyManagementException | NoSuchAlgorithmException var5) {
            throw new RuntimeException(var5);
        }
    }
}
